Okapi BM25

Okapi BM25 是經典資訊檢索模型之一,也是 Elasticsearch Elasticsearch similarity 計算檢索相關性分數的預設演算法。BM 是 Best Matching 的縮寫,25 是其實驗參數配置所實驗的次數。

BM25 算法淺析

BM25 的模型算式如下

其中 query Q 包含 terms
此算式中包含六個部分: 分別說明如下:

在 Elasticsearch 中的 Okapi BM25

由於 Elasticsearch 預設會使用 5 個 Elasticsearch primary shard 作為一個 Elasticsearch index,每一個 shard 未必會收到所有的文件,因此在使用 BM25 作為計算的 相關性分數 時會受到其所考慮的文件並非是全局的而受到偏誤,例如在 shard 1 的文件較多則 IDF 所計算的分數可能就較其他文件較少的 shard 分數較低而誤判排序結果。

解決的方法有三:

於 Elasticsearch 中調整 Okapi BM25 參數

在進行調整 參數之前,還有很多修正是可以讓 Elasticsearch 的搜尋結果更貼近使用者意圖:

若這些都調教過,調整 參數才是加強搜尋結果的最後一哩路。首先要認知到並沒有最佳的唯一的 組合,需視使用情境以進行調整:

  1. 首先要先知道 的可以調整範圍區間為何,在過去的實驗中證實較好的 的範圍分別是:
    • 常見設定在 0 - 3 之間,設定更高也是可行的,然而過去的實驗多數是設定在 0.5 - 2 之間有比較好的結果,調整方式多為增減 0.1 - 0.2 。
    • 的範圍區間為 0 - 1 之間,多數調整的方式為增減 0.1,多數實驗顯示將 設定在 0.3 - 0.9 有較好的結果。
  2. 接著要了解自己的搜尋情境適合什麼樣的參數組合:
    • 針對 需要了解的是搜尋的情境是否認為匹配到的文章需要設定 term frequency 的飽和度,即使用者意圖是否會認為單篇文章出現越大量匹配到的詞會是好的結果,若搜尋的 corpus 多為長文如書籍、文章等,便有可能有較高的 term frequency 那麼 便需要設定高一些才能顯現差異性,然而若用於搜尋商品敘述等,則頻繁出現匹配的 term 是一件比較奇怪的事情,則可以將 調低一些。
    • 針對 需要認知到搜尋的情境是否考慮到搜尋的情境會希望如何處理長文的搜尋結果,並且考慮文章的長短要如何影響詞的相關性。例如針對特定專長的技術文章,往往文章內容是有針對性的那麼文章的長度不論多長會是可以容忍的,反之文本的內容多是品質參差不齊的內文的話,越長的文章可能會是垃圾資訊或是無關的資訊,則可以調高 以進行扣分。

Explain API

在 Elasticsearch 中提供了 Explain API 解釋為什麼搜尋結果的給分是怎麼給的,只需要在一般的 query endpoint 後再加入 _explain 即可,例如

GET /people3/_doc/4/_explain
{
  "query": {
    "match": {
      "title": "shane connelly"
    }
  }
}

會得到如下結果:

{
  "_index": "people3",
  "_type": "_doc",
  "_id": "4",
  "matched": true,
  "explanation": {
    "value": 0.71437943,
    "description": "sum of:",
    "details": [
        {
          "value": 0.102611035,
          "description": "weight(title:shane in 3) [PerFieldSimilarity], result of:",
          "details": [
            {
              "value": 0.102611035,
              "description": "score(doc=3,freq=1.0 = termFreq=1.0\n), product of:",
              "details": [
                {
                  "value": 0.074107975,
                  "description": "idf, computed as log(1 + (docCount - docFreq + 0.5) / (docFreq + 0.5)) from:",
                  "details": [
                    {
                      "value": 6,
                      "description": "docFreq",
                      "details": []
                    }, {
                      "value": 6,
                      "description": "docCount",
                      "details": []
                    }
                  ]
				},
				{
                  "value": 1.3846153,
                  "description": "tfNorm, computed as (freq * (k1 + 1)) / (freq + k1 * (1 - b + b * fieldLength / avgFieldLength)) from:",
                  "details": [
                    {
                      "value": 1,
                      "description": "termFreq=1.0",
                      "details": []
                    },
					{
                      "value": 5,
					  "description": "parameter k1",
					  "details": []
					},
					{
					  "value": 1,
					  "description": "parameter b",
					  "details": []
				    },
					{
					  "value": 3,
					  "description": "avgFieldLength",
					  "details": []
					},
					{
					  "value": 2,
					  "description": "fieldLength",
					  "details": []
				    }
				  ]
				}
			  ]
		    }
	      ]
        },
  // others terms result...
  ]
}

可以清楚地看到每個 query term 會是怎麼在 BM25 中計算出來,包含參數的設定、IDF 值為多少,tfNorm 值為多少等,而 _explain 是 debug 工具,切記要在 production 模式關閉。